/*!
 * Copyright © 2016-2017 European Support Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

import React from 'react';
import PropTypes from 'prop-types';
import Common from '../../common/Common';
import Logger from '../../common/Logger';
import Diagram from '../diagram/Diagram';
import Dialog from '../dialog/Dialog';
import Editor from '../editor/Editor';
import Export from '../export/Export';
import Overlay from '../overlay/Overlay';

/**
 * Application controller, also a view.
 */
class Application extends React.Component {
    /**
     * Construct application view.
     * @param props element properties.
     * @param context react context.
     */
    constructor(props, context) {
        super(props, context);

        this.sequencer = Common.assertNotNull(props.sequencer);
        this.model = this.sequencer.getModel();
        this.metamodel = this.sequencer.getMetamodel();
        this.options = props.options;
        Logger.setLevel(this.options.unwrap().log.level);

        // Bindings.

        this.showInfoDialog = this.showInfoDialog.bind(this);
        this.showEditDialog = this.showEditDialog.bind(this);
        this.showConfirmDialog = this.showConfirmDialog.bind(this);
        this.hideOverlay = this.hideOverlay.bind(this);
        this.onMouseMove = this.onMouseMove.bind(this);
        this.onMouseUp = this.onMouseUp.bind(this);
    }

    // ///////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Get application options.
     * @returns JSON options, see Options.js.
     */
    getOptions() {
        return this.options.unwrap();
    }

    // ///////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Set diagram name.
     * @param n diagram (human-readable) name.
     */
    setName(n) {
        this.diagram.setName(n);
    }

    // ///////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Set diagram model.
     * @param model diagram instance.
     */
    setModel(model) {
        Common.assertNotNull(model);

        this.model = model;

        if (this.editor) {
            this.editor.render();
        }

        if (this.diagram) {
            this.diagram.render();
        }
    }

    // ///////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Get Model wrapper.
     * @returns Model.
     */
    getModel() {
        return this.model;
    }

    // ///////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Get SVG element.
     * @returns {*}
     */
    getSVG() {
        return this.diagram.getSVG();
    }

    // ///////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Get top-level widget. Provides the demo toolbar with access to the public API.
     * @returns {*}
     */
    getSequencer() {
        return this.sequencer;
    }

    // ///////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Present info dialog.
     * @param msg info message.
     */
    showInfoDialog(msg) {
        this.dialog.showInfoDialog(msg);
    }

    // ///////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Present error dialog.
     * @param msg error message.
     */
    showErrorDialog(msg) {
        this.dialog.showErrorDialog(msg);
    }

    // ///////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Present confirmation dialog.
     * @param msg info message.
     * @param cb callback function to be invoked on OK.
     */
    showConfirmDialog(msg, cb) {
        Common.assertType(cb, 'Function');
        this.dialog.showConfirmDialog(msg, cb);
    }

    // ///////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Present edit (textarea) dialog.
     * @param msg prompt.
     * @param text current edit text.
     * @param cb callback function to be invoked on OK, taking the updated text
     * as an argument.
     */
    showEditDialog(msg, text, cb) {
        this.dialog.showEditDialog(msg, text, cb);
    }

    // ///////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Select lifeline by ID.
     * @param id lifeline ID.
     */
    selectLifeline(id) {
        if (this.editor) {
            this.editor.selectLifeline(id);
        }
        if (this.diagram) {
            this.diagram.selectLifeline(id);
        }
    }

    // ///////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Select message by ID.
     * @param id message ID.
     */
    selectMessage(id) {
        if (this.editor) {
            this.editor.selectMessage(id);
        }
        if (this.diagram) {
            this.diagram.selectMessage(id);
        }
    }

    // ///////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * (Re)render just the diagram.
     */
    renderDiagram() {
        this.diagram.redraw();
    }

    // ///////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Show overlay between application and modal dialog.
     */
    showOverlay() {
        if (this.overlay) {
            this.overlay.setVisible(true);
        }
    }

    // ///////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Hide overlay between application and modal dialog.
     */
    hideOverlay() {
        if (this.overlay) {
            this.overlay.setVisible(false);
        }
    }

    // ///////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Capture mouse move events, for resize.
     * @param event move event.
     */
    onMouseMove(event) {
        if (this.editor) {
            this.editor.onMouseMove(event);
        }
    }

    // ///////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Propagate mouse event to the editor that manages the resize.
     */
    onMouseUp() {
        if (this.editor) {
            this.editor.onMouseUp();
        }
    }

    // ///////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Render current model state.
     */
    render() {
        return (
            <div
                className="asdcs-control"
                onMouseMove={this.onMouseMove}
                onMouseUp={this.onMouseUp}>
                <Editor
                    application={this}
                    ref={r => {
                        this.editor = r;
                    }}
                />
                <Diagram
                    application={this}
                    ref={r => {
                        this.diagram = r;
                    }}
                />
                <Dialog
                    application={this}
                    ref={r => {
                        this.dialog = r;
                    }}
                />
                <Export />
                <Overlay
                    application={this}
                    ref={r => {
                        this.overlay = r;
                    }}
                />
            </div>
        );
    }
}

/** React properties. */
Application.propTypes = {
    options: PropTypes.object.isRequired,
    sequencer: PropTypes.object.isRequired
};

export default Application;
